﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Microsoft.Forums.OfflineStore;
using Microsoft.Forums.Tests.HelperTools;
using Microsoft.Forums.Tests.HelperTools.Stubs;
using Microsoft.Forums.ForumsClient.Models;
using System.IO;
using System.ComponentModel;
using Microsoft.Forums.ForumsClient;

namespace ForumsClientUnitTests
{
    /// <summary>
    /// Test the ObjectRegistryTests class
    /// </summary>
    [TestFixture]
    public class ObjectRegistryTests
    {

        /// <summary>
        /// Initialized the database
        /// </summary>
        [TestFixtureSetUp]
        public void SetupFixture()
        {
            DatabaseHelper.SetupDatabase();
        }

        /// <summary>
        /// Setup stubs and datamodel before each test
        /// </summary>
        [SetUp]
        public void Setup()
        {
            var connectionStub = new ConnectionStub();
            var syncStub = new SyncControllerStub();
            var subscribedForums = new SubscribedForumsCollection();
            var manager = new SynchronizationManager(syncStub);
            var providerStub = new ForumsProviderStub();

            AppDataModel.Initialize(providerStub, manager, connectionStub, DatabaseHelper.DatabaseFileName, subscribedForums);
        }

        /// <summary>
        /// After test is complete remove all data we added from the database
        /// 
        /// Ideally, we would just be able to recreate the database but there is an odd 
        /// file locked issue that happens intermittently
        /// </summary
        [TearDown]
        public void Teardown()
        {
            DatabaseHelper.ClearDatabase();
        }


        /// <summary>
        /// Loads the forum into object registry.
        /// </summary>
        private void LoadForumIntoObjectRegistry()
        {
            LoadDataIntoDatabase();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Forum forum = db.Forums.Single(x => x.Name == "Forum1");
            ObjectRegistry.AddForum(forum);
        }

        /// <summary>
        /// Loads the data into database.
        /// </summary>
        private void LoadDataIntoDatabase()
        {
            ForumDataCreator creator = new ForumDataCreator(DatabaseHelper.GetNewDataContext());
            creator.Load("RootPostTest.xml");
        }

        #region Get/Add/Update Post Tests

        /// <summary>
        /// Add post takes a post id and gets the post from the database and adds it to the registry
        /// </summary>
        [Test]
        public void AddPostAddsPostToRegistryGivenPostId()
        {
            LoadForumIntoObjectRegistry();
            var db = DatabaseHelper.GetNewDataContext();
            var post = db.Posts.Single(x => x.Subject == "Post1");

            Post addedPost = ObjectRegistry.AddPost(post.Id);

            Assert.AreEqual(post.Id, addedPost.Id);
            Assert.IsFalse(Object.ReferenceEquals(post,addedPost));
            
        }

        /// <summary>
        /// Add post returns null if post id is not in database
        /// </summary>
        [Test]
        public void AddPostReturnsNullIfPostIsNotInDatabase()
        {
            Post addedPost = ObjectRegistry.AddPost(Guid.NewGuid());
            Assert.IsNull(addedPost);
        }

        /// <summary>
        /// Add post throws exception if post object is null
        /// </summary>
        [ExpectedException(typeof(ArgumentNullException))]
        [Test]
        public void AddPostThrowsArgumentExceptionIfPostIsNull()
        {
            Post addedPost = ObjectRegistry.AddPost(null);
        }

        /// <summary>
        /// Add post throws exception if post object's post property set is null
        /// </summary>
        [ExpectedException(typeof(ArgumentNullException))]
        [Test]
        public void AddPostThrowsArgumentExceptionIfPostHasNoPostPropertySet()
        {
            Post post = new Post();
            Post addedPost = ObjectRegistry.AddPost(post);
        }

        /// <summary>
        /// Add post returns same pos object if not in registry yet
        /// </summary>
        [Test]
        public void AddPostAddsPostToRegistryGivenPostObject()
        {
            LoadForumIntoObjectRegistry();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Post post = db.Posts.Single(x => x.Subject == "Post1");

            Post addedPost = ObjectRegistry.AddPost(post);

            Assert.AreEqual(post.Id, addedPost.Id);
            Assert.IsTrue(Object.ReferenceEquals(post, addedPost));

        }

        /// <summary>
        /// Update Post updates registry post object
        /// </summary>
        [Test]
        public void UpdatePostWillUpdateTheRegistryPostObject()
        {
            LoadForumIntoObjectRegistry();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Post post = db.Posts.Single(x => x.Subject == "Post1");
            Post addedPost = ObjectRegistry.AddPost(post.Id);
            post.Subject = "NEW SUBJECT";
            db.SubmitChanges();

            ObjectRegistry.UpdatePost(post.ForumId, post.Id);

            Assert.AreEqual(post.Subject,addedPost.Subject);

        }

        /// <summary>
        /// GetPost Gets Post From Registry Given Id
        /// </summary>
        [Test]
        public void GetPostGetsPostFromRegistryGivenId()
        {
            LoadForumIntoObjectRegistry();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Post post = db.Posts.Single(x => x.Subject == "Post1");
            Post addedPost = ObjectRegistry.AddPost(post.Id);

            Post gotPost = ObjectRegistry.GetPost(post.ForumId, post.Id);

            Assert.IsNotNull(gotPost);
            Assert.IsTrue(Object.ReferenceEquals(addedPost, gotPost));

        }

        /// <summary>
        /// GetPost Adds post to registry if not currently in registry
        /// </summary>
        [Test]
        public void GetPostAddsPostToRegistryAndReturnsItIfNotInRegistryGivenPostId()
        {
            LoadForumIntoObjectRegistry();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Post post = db.Posts.Single(x => x.Subject == "Post1");

            Post gotPost = ObjectRegistry.GetPost(post.ForumId, post.Id);

            Assert.IsNotNull(gotPost);
            Assert.AreEqual(post.Id, gotPost.Id);

        }

        /// <summary>
        /// GetPost Gets Post From Registry Given PostObject
        /// </summary>
        [Test]
        public void GetPostGetsPostFromRegistryGivenPostObject()
        {
            LoadForumIntoObjectRegistry();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Post post = db.Posts.Single(x => x.Subject == "Post1");
            Post addedPost = ObjectRegistry.AddPost(post.Id);

            Post gotPost = ObjectRegistry.GetPost(post);

            Assert.IsNotNull(gotPost);
            Assert.IsTrue(Object.ReferenceEquals(addedPost, gotPost));

        }

        /// <summary>
        /// GetPost Adds post to registry if not currently in registry
        /// </summary>
        [Test]
        public void GetPostAddsPostToRegistryAndReturnsItIfNotInRegistryGivenPostObject()
        {
            LoadForumIntoObjectRegistry();
            var db = DatabaseHelper.GetNewDataContext();
            var post = db.Posts.Single(x => x.Subject == "Post1");

            Post gotPost = ObjectRegistry.GetPost(post);

            Assert.IsNotNull(gotPost);
            Assert.AreEqual(post.Id, gotPost.Id);

        }
        #endregion


        #region Get/Add/Update Thread Tests

        /// <summary>
        /// Add Thread takes a Thread id and gets the Thread from the database and adds it to the registry
        /// </summary>
        [Test]
        public void AddThreadAddsThreadToRegistryGivenThreadId()
        {
            LoadForumIntoObjectRegistry();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Thread Thread = db.Threads.Single(x => x.Topic == "Thread1");

            Thread addedThread = ObjectRegistry.AddThread(Thread.Id);

            Assert.AreEqual(Thread.Id, addedThread.Id);
            Assert.IsFalse(Object.ReferenceEquals(Thread, addedThread));

        }

        /// <summary>
        /// Add Thread throws exception if Thread id is not in database
        /// </summary>
        [ExpectedException(typeof(ArgumentException))]
        [Test]
        public void AddThreadThrowsArgumentExceptionIfThreadIsNotInDatabase()
        {
            Thread addedThread = ObjectRegistry.AddThread(Guid.NewGuid());
        }

        /// <summary>
        /// Add Thread throws exception if Thread object is null
        /// </summary>
        [ExpectedException(typeof(ArgumentNullException))]
        [Test]
        public void AddThreadThrowsArgumentExceptionIfThreadIsNull()
        {
            Thread addedThread = ObjectRegistry.AddThread(null);
        }

        /// <summary>
        /// Add Thread throws exception if Thread object's Thread property set is null
        /// </summary>
        [ExpectedException(typeof(ArgumentNullException))]
        [Test]
        public void AddThreadThrowsArgumentExceptionIfThreadHasNoThreadPropertySet()
        {
            Thread Thread = new Thread();
            Thread addedThread = ObjectRegistry.AddThread(Thread);
        }

        /// <summary>
        /// Add Thread returns same pos object if not in registry yet
        /// </summary>
        [Test]
        public void AddThreadAddsThreadToRegistryGivenThreadObject()
        {
            LoadForumIntoObjectRegistry();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Thread Thread = db.Threads.Single(x => x.Topic == "Thread1");

            Thread addedThread = ObjectRegistry.AddThread(Thread);

            Assert.AreEqual(Thread.Id, addedThread.Id);
            Assert.IsTrue(Object.ReferenceEquals(Thread, addedThread));

        }

        /// <summary>
        /// Update Thread updates registry Thread object
        /// </summary>
        [Test]
        public void UpdateThreadWillUpdateTheRegistryThreadObject()
        {
            LoadForumIntoObjectRegistry();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Thread Thread = db.Threads.Single(x => x.Topic == "Thread1");
            Thread addedThread = ObjectRegistry.AddThread(Thread.Id);
            Thread.Topic = "NEW TOPIC";
            db.SubmitChanges();

            ObjectRegistry.UpdateThread(Thread.ForumId, Thread.Id);

            Assert.AreEqual(Thread.Topic, addedThread.Topic);

        }

        /// <summary>
        /// GetThread Gets Thread From Registry Given Id
        /// </summary>
        [Test]
        public void GetThreadGetsThreadFromRegistryGivenId()
        {
            LoadForumIntoObjectRegistry();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Thread Thread = db.Threads.Single(x => x.Topic == "Thread1");
            Thread addedThread = ObjectRegistry.AddThread(Thread.Id);

            Thread gotThread = ObjectRegistry.GetThread(Thread.ForumId, Thread.Id);

            Assert.IsNotNull(gotThread);
            Assert.IsTrue(Object.ReferenceEquals(addedThread, gotThread));

        }

        /// <summary>
        /// GetThread Adds Thread to registry if not currently in registry
        /// </summary>
        [Test]
        public void GetThreadAddsThreadToRegistryAndReturnsItIfNotInRegistryGivenThreadId()
        {
            LoadForumIntoObjectRegistry();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Thread Thread = db.Threads.Single(x => x.Topic == "Thread1");

            Thread gotThread = ObjectRegistry.GetThread(Thread.ForumId, Thread.Id);

            Assert.IsNotNull(gotThread);
            Assert.AreEqual(Thread.Id, gotThread.Id);

        }

        /// <summary>
        /// GetThread Gets Thread From Registry Given ThreadObject
        /// </summary>
        [Test]
        public void GetThreadGetsThreadFromRegistryGivenThreadObject()
        {
            LoadForumIntoObjectRegistry();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Thread Thread = db.Threads.Single(x => x.Topic == "Thread1");
            Thread addedThread = ObjectRegistry.AddThread(Thread.Id);

            Thread gotThread = ObjectRegistry.GetThread(Thread);

            Assert.IsNotNull(gotThread);
            Assert.IsTrue(Object.ReferenceEquals(addedThread, gotThread));

        }

        /// <summary>
        /// GetThread Adds Thread to registry if not currently in registry
        /// </summary>
        [Test]
        public void GetThreadAddsThreadToRegistryAndReturnsItIfNotInRegistryGivenThreadObject()
        {
            LoadForumIntoObjectRegistry();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Thread Thread = db.Threads.Single(x => x.Topic == "Thread1");

            Thread gotThread = ObjectRegistry.GetThread(Thread);

            Assert.IsNotNull(gotThread);
            Assert.AreEqual(Thread.Id, gotThread.Id);

        }
        #endregion


        #region Get/Add/Update Forum Tests

        /// <summary>
        /// Add Forum takes a Forum id and gets the Forum from the database and adds it to the registry
        /// </summary>
        [Test]
        public void AddForumAddsForumToRegistryGivenForumId()
        {
            LoadDataIntoDatabase();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Forum Forum = db.Forums.Single(x => x.Name == "Forum1");

            Forum addedForum = ObjectRegistry.AddForum(Forum.Id);

            Assert.AreEqual(Forum.Id, addedForum.Id);
            Assert.IsFalse(Object.ReferenceEquals(Forum, addedForum));

        }

        /// <summary>
        /// Add Forum throws exception if Forum id is not in database
        /// </summary>
        [ExpectedException(typeof(ArgumentException))]
        [Test]
        public void AddForumThrowsArgumentExceptionIfForumIsNotInDatabase()
        {
            Forum addedForum = ObjectRegistry.AddForum(Guid.NewGuid());
        }

        /// <summary>
        /// Add Forum throws exception if Forum object is null
        /// </summary>
        [ExpectedException(typeof(ArgumentNullException))]
        [Test]
        public void AddForumThrowsArgumentExceptionIfForumIsNull()
        {
            Forum addedForum = ObjectRegistry.AddForum(null);
        }

        /// <summary>
        /// Add Forum throws exception if Forum object's Forum property set is null
        /// </summary>
        [ExpectedException(typeof(ArgumentNullException))]
        [Test]
        public void AddForumThrowsArgumentExceptionIfForumHasNoForumPropertySet()
        {
            Forum Forum = new Forum();
            Forum addedForum = ObjectRegistry.AddForum(Forum);
        }

        /// <summary>
        /// Add Forum returns same pos object if not in registry yet
        /// </summary>
        [Test]
        public void AddForumAddsForumToRegistryGivenForumObject()
        {
            LoadDataIntoDatabase();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Forum Forum = db.Forums.Single(x => x.Name == "Forum1");

            Forum addedForum = ObjectRegistry.AddForum(Forum);

            Assert.AreEqual(Forum.Id, addedForum.Id);
            Assert.IsTrue(Object.ReferenceEquals(Forum, addedForum));

        }

        /// <summary>
        /// Update Forum updates registry Forum object
        /// </summary>
        [Test]
        public void UpdateForumWillUpdateTheRegistryForumObject()
        {
            LoadDataIntoDatabase();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Forum Forum = db.Forums.Single(x => x.Name == "Forum1");
            Forum addedForum = ObjectRegistry.AddForum(Forum.Id);
            Forum.Name = "NEW Name";
            db.SubmitChanges();

            ObjectRegistry.UpdateForum(Forum.Id);

            Assert.AreEqual(Forum.Name, addedForum.Name);

        }

        /// <summary>
        /// GetForum Gets Forum From Registry Given Id
        /// </summary>
        [Test]
        public void GetForumGetsForumFromRegistryGivenId()
        {
            LoadDataIntoDatabase();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Forum Forum = db.Forums.Single(x => x.Name == "Forum1");
            Forum addedForum = ObjectRegistry.AddForum(Forum.Id);

            Forum gotForum = ObjectRegistry.GetForum(Forum.Id);

            Assert.IsNotNull(gotForum);
            Assert.IsTrue(Object.ReferenceEquals(addedForum, gotForum));

        }

        /// <summary>
        /// GetForum Adds Forum to registry if not currently in registry
        /// </summary>
        [Test]
        public void GetForumAddsForumToRegistryAndReturnsItIfNotInRegistryGivenForumId()
        {
            LoadDataIntoDatabase();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Forum Forum = db.Forums.Single(x => x.Name == "Forum1");

            Forum gotForum = ObjectRegistry.GetForum(Forum.Id);

            Assert.IsNotNull(gotForum);
            Assert.AreEqual(Forum.Id, gotForum.Id);

        }

        /// <summary>
        /// GetForum Gets Forum From Registry Given ForumObject
        /// </summary>
        [Test]
        public void GetForumGetsForumFromRegistryGivenForumObject()
        {
            LoadDataIntoDatabase();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Forum Forum = db.Forums.Single(x => x.Name == "Forum1");
            Forum addedForum = ObjectRegistry.AddForum(Forum.Id);

            Forum gotForum = ObjectRegistry.GetForum(Forum);

            Assert.IsNotNull(gotForum);
            Assert.IsTrue(Object.ReferenceEquals(addedForum, gotForum));

        }

        /// <summary>
        /// GetForum Adds Forum to registry if not currently in registry
        /// </summary>
        [Test]
        public void GetForumAddsForumToRegistryAndReturnsItIfNotInRegistryGivenForumObject()
        {
            LoadDataIntoDatabase();
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Forum Forum = db.Forums.Single(x => x.Name == "Forum1");

            Forum gotForum = ObjectRegistry.GetForum(Forum);

            Assert.IsNotNull(gotForum);
            Assert.AreEqual(Forum.Id, gotForum.Id);

        }
        #endregion
    }
}
